package cate.game.role;

import cate.game.db.GameMongoDao;
import cate.game.log.GameLoggerFactory;
import cate.game.role.x.CacheRolePartWorker;
import cate.game.role.x.RolePartWorker;
import org.springframework.data.mongodb.repository.MongoRepository;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Optional;
import java.util.concurrent.locks.ReentrantLock;

@SuppressWarnings("unchecked")
public abstract class RolePartGetter<P extends RolePart> extends RoleDependent{
    protected P part;

    RolePartWorker<P> worker;

    public RolePartGetter(Role role) {
        super(role);
        this.worker = new CacheRolePartWorker<>();
        this.worker.setup(role);
    }

    protected P buildPart(){
        Type superclass = getClass().getGenericSuperclass();
        ParameterizedType parameterizedType;
        if (superclass instanceof ParameterizedType) {
            parameterizedType = (ParameterizedType) superclass;
            Type[] typeArray = parameterizedType.getActualTypeArguments();
            if (typeArray != null && typeArray.length > 0) {
                Class<P> clazz = (Class<P>) typeArray[0];
                try{
                    return clazz.getDeclaredConstructor(Role.class).newInstance(role);
                }catch(Exception e){
                    logger.error("生成{}实例失败",clazz.getName(),e);
                }
            }
        }
        return null;
    }

    protected abstract MongoRepository<P, String> getRepo(GameMongoDao dao);

    protected MongoRepository<P, String> repo() {
        return getRepo(this.role.getGame().facilities.getMongo());
    }

    public P get() {
        return get(true);
    }

    public P getNew(){
        return get(false, true);
    }

    public P get(boolean createWhenNull) {
        return get(createWhenNull, false);
    }

    public P get(boolean createWhenNull, boolean directCreate) {
        if(this.part == null) {
            //锁粒度升级，防止多线程分别获取不同part的锁
            ReentrantLock lock = role.getGame().role.getLock(role.getUid());
            lock.lock();
            try{
                if(this.part == null){
                    this.logger = GameLoggerFactory.getLogger(role.getGame(), this.getClass());
                    if(directCreate){
                        this.part = buildPart();
                    }else{
                        Optional<P> one = worker.findById(repo(), this.role.getUid());
                        this.part = one.orElse(createWhenNull ? buildPart() : null);
                    }
                    if(this.part != null) {
                        this.part.initialize(role);
                    }
                }
            }catch (Exception e){
                logger.error("获取部件异常", e);
            }finally {
                lock.unlock();
            }
        }
        return this.part;
    }

    public void save(){
        if(this.part != null) {
            ReentrantLock lock = role.getGame().role.getLock(role.getUid());
            lock.lock();
            try{
                if(this.part != null){
                   worker.save(repo(), this.part);
                    //log.info("玩家保存完成 {}: {}", this.part.getClass(),this.part.uid);
                }
            } catch(Throwable e) {
                logger.error("", e);
            } finally {
                lock.unlock();
            }
        }
    }
}
